Completed
Push — master ( b6e14c...5630cf )
by
unknown
53s
created

server.js ➔ ???   B

Complexity

Conditions 1
Paths 4

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1.5584

Importance

Changes 13
Bugs 2 Features 0
Metric Value
cc 1
c 13
b 2
f 0
nc 4
dl 0
loc 33
ccs 3
cts 17
cp 0.1765
crap 1.5584
rs 8.8571
nop 0

1 Function

Rating   Name   Duplication   Size   Complexity  
B server.js ➔ ... ➔ ??? 0 25 3
1
/**
2
 * Server main
3
 *
4
 * @since 1.0.0
5
 *
6
 */
7
8 1
const Hapi = require('hapi');
9
10 1
const vision = require('vision');
11 1
const inert = require('inert');
12 1
const HapiAuthJwt2 = require('hapi-auth-jwt2');
13 1
const HapiReactViews = require('hapi-react-views');
14 1
const HapiErrorHandler = require('./middleware/error-handler');
15 1
const HapiTransformer = require('./middleware/transformer');
16 1
const HapiAuthChecker = require('./middleware/auth-info-checker');
17
18 1
const apiRouter = require('./router/api-router');
19 1
const baseRouter = require('./router/ui-router');
20 1
const User = require('./repository/User');
21 1
const DeviceType = require('./repository/DeviceType');
22 1
const StatusType = require('./repository/StatusType');
23
24 1
const config = require('./config/server.config');
25 1
const NotifierError = require('./common/Error');
26
27 1
const util = require('./common/common-util');
28 1
const logger = require('winston');
29
30
// For JSX transpiling
31 1
require('babel-register');
32 1
require('babel-polyfill');
33
34 1
const server = new Hapi.Server();
35 1
server.connection({ port: process.env.PORT || config.defaults.port });
36
37 1
server.state('token', {
38
  ttl: config.auth.tokenTTL,
39
  isSecure: process.env.USE_HTTPS && process.env.USE_HTTPS === 'true',
40
  path: '/',
41
});
42
43 1
const plugins = [
44
  { register: vision },
45
  { register: inert },
46
  { register: HapiAuthJwt2 },
47
  { register: HapiErrorHandler, options: { apiPrefix: config.url.apiPrefix, errorView: 'Error' } },
48
  { register: HapiAuthChecker,
49
    options: {
50
      excludeUrlPatterns: [new RegExp(`^${config.url.apiPrefix}`), new RegExp('^/logout')],
51
    },
52
  },
53
  { register: HapiTransformer, options: { apiPrefix: config.url.apiPrefix } },
54
];
55
56 1
const _setAuthStrategy = () => {
57 1
  server.auth.strategy('jwt', 'jwt', {
58
    key: process.env.SECRET_KEY || config.auth.secretKey,
59
    validateFunc: (decoded, request, callback) => {
60
      // Check token IP address
61
      const clientIP = util.getClientIp(request);
62
      if (clientIP !== decoded.ip) {
63
        logger.warn(`[Auth] This client IP is matched with token info.: decoded.ip => ${decoded.ip}, client IP => ${clientIP}`);
64
        return callback(new NotifierError(NotifierError.Types.AUTH_TOKEN_INVALID), false);
65
      }
66
      // Check token expiration
67
      if (decoded.exp < new Date().getTime()) {
68
        logger.warn(`[Auth] This auth token is expired.: decoded.exp => ${decoded.exp}, now => ${new Date().getTime()}`);
69
        return callback(new NotifierError(NotifierError.Types.AUTH_TOKEN_EXPIRED), false);
70
      }
71
      return User.find({ username: decoded.username })
72
        .then((accounts) => {
73
          if (!accounts || accounts.length === 0) {
74
            logger.warn(`[Auth] This account is not exist.: ${decoded.username}`);
75
            return callback(new NotifierError(NotifierError.Types.AUTH_USER_NOT_EXIST, { username: decoded.username }), false);
76
          }
77
          return callback(null, true, accounts[0]);
78
        })
79
        .catch((e) => {
80
          logger.error(`[DB] DB error occurred: ${e.message}`);
81
          callback(new NotifierError(NotifierError.Types.DB), false);
82
        });
83
    },
84
    verifyOptions: { algorithms: ['HS256'] },
85
  });
86
87 1
  server.auth.default('jwt');
88
};
89
90 1
const _setViewEngine = () => {
91 1
  server.views({
92
    engines: { jsx: HapiReactViews },
93
    relativeTo: __dirname,
94
    path: config.directory.component,
95
  });
96
};
97
98 1
const _setRoutes = (extraRoutes) => {
99
  // for static assets
100 1
  server.route({
101
    method: 'GET',
102
    path: `${config.url.publicPrefix}/{param*}`,
103
    handler: {
104
      directory: {
105
        path: config.directory.public,
106
        listing: false,
107
      },
108
    },
109
    config: {
110
      auth: false,
111
    },
112
  });
113 1
  server.route(apiRouter);
114 1
  server.route(baseRouter);
115 1
  if (extraRoutes) {
116
    server.route(extraRoutes);
117
  }
118
};
119
120 1
const _setInitalData = () => {
121 1
  let promises = [];
122 1
  return Promise.all([User.count(), DeviceType.count(), StatusType.count()])
123
    .then(([userCount, deviceTypeCount, statusTypeCount]) => {
124 1
      if (userCount === 0) {
125
        promises = promises.concat(config.initialData.users.map(user => User.add(user)));
126
      }
127 1
      if (deviceTypeCount === 0) {
128 4
        promises = promises.concat(config.initialData.deviceTypes.map(dt => DeviceType.add(dt)));
129
      }
130 1
      if (statusTypeCount === 0) {
131 2
        promises = promises.concat(config.initialData.statusTypes.map(user => StatusType.add(user)));
132
      }
133
    })
134 1
    .then(() => Promise.all(promises))
135 1
    .then(result => logger.log('[SET INITIAL DATA] success: ', result));
136
};
137
138 1
exports.addPlugin = (pluginSetting) => {
139
  plugins.push(pluginSetting);
140
};
141
142 1
exports.start = extraRoutes => server.register(plugins)
143
    .then(() => {
144 1
      _setAuthStrategy();
145 1
      _setViewEngine();
146 1
      _setRoutes(extraRoutes);
147
    })
148 1
    .then(() => _setInitalData())
149 1
    .then(() => server.start())
150
    .then(() => {
151 1
      logger.log('Server running at:', server.info.uri);
152 1
      return server;
153
    })
154
    .catch((error) => { throw error; });
155